//-----------------------------------------------------------------------
// <copyright file="GetUsersForChangelists.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Giles Knap [Updated 03 Apr 08: Mike Fourie - Code Cleanup, TFS 2008 Support, Workingdirectory Support etc.]</author>
// <date>2006-03-23</date>
// <summary>Returns a list of users between two changelist numbers for a Team Source filespec.</summary>
//-----------------------------------------------------------------------
namespace Microsoft.Sdc.Tasks.SourceTfs
{
    using System.Globalization;
    using System.Collections;
    using System;
    using Microsoft.Build.Framework;
    using Microsoft.Sdc.Tasks.Configuration;

    /// <summary>
    /// Returns a list of users between two changelist numbers for a Team Source filespec.
    /// </summary>
    /// <remarks>
    /// <code><![CDATA[<SourceTfs.GetUsersForChangelists Path="path" Start="start" End="end" ExcludedUsers="excludedUsers" TfsVersion="tfsVersion"/>]]></code>
    /// <para>where:</para>
    /// <para><i>path (Required)</i></para>
    /// <para>The depot path to check under.</para>
    /// <para><i>start (Required)</i></para>
    /// <para>The start changelist number.</para>
    /// <para><i>end (Required)</i></para>
    /// <para>The end changelist number.</para>
    /// <para><i>excludedUsers</i></para>
    /// <para>The list of users to exclude from the returning list.</para>
    /// <para><i>tfsVersion</i></para>
    /// <para>Version of TFS to use. Supports 2005 and 2008. Default is 2005</para>
    /// </remarks>
    /// <example>
    /// <code><![CDATA[
    /// <Project>
    ///     <Target Name="Test" >
    ///         <SourceTfs.GetUsersForChangelists
    ///             Path="//depot/Office/Main/Src/..."
    ///             Start="1056"
    ///             End="1067" >
    ///              <Output TaskParameter="Users" PropertyName="Users" />
    ///         </SourceTfs.GetUsersForChangelists>
    ///         <Message Text="The users with changes in the depot between those changelist numbers are @(Users)" />
    ///     </Target>
    /// </Project>
    /// ]]></code>    
    /// </example>
    public class GetUsersForChangelists : TaskBase
    {
        private string path;
        private int start;
        private int end;
        private string[] users;
        private string[] excludedUsers;
        private string tfsVersion = "2005";

        /// <summary>
        /// Gets or sets the TFS version.
        /// </summary>
        /// <value>The TFS version.</value>
        public string TfsVersion
        {
            get { return this.tfsVersion; }
            set { this.tfsVersion = value; }
        }

        /// <summary>
        /// Gets or sets the users.
        /// </summary>
        /// <value>The users.</value>
        [Output]
        public string[] Users
        {
            get { return (this.users ?? new string[0]); }
            set { this.users = value; }
        }

        /// <summary>
        /// Gets or sets the User names not to include in the mailing list
        /// </summary>
        public string[] ExcludedUsers
        {
            get
            {
                return (this.excludedUsers ?? new string[0]);
            }

            set
            {
                this.excludedUsers = value;
                Array.Sort(this.excludedUsers);
            }
        }

        /// <summary>
        /// Gets or sets the depot path.
        /// </summary>
        /// <value>The full path for the depot.</value>
        [Required]
        public string Path
        {
            get { return this.path; }
            set { this.path = value; }
        }

        /// <summary>
        /// Gets or sets the changelist number to start from
        /// </summary>
        [Required]
        public int Start
        {
            get { return this.start; }
            set { this.start = value; }
        }

        /// <summary>
        /// Gets or sets the changelist number to end at
        /// </summary>
        public int End
        {
            get { return this.end == 0 ? this.start : this.end; }
            set { this.end = value; }
        }

        /// <summary>
        /// Performs the action of this task.
        /// </summary>
        protected override void InternalExecute()
        {
            // Max number of users
            Hashtable usersHash = new Hashtable(this.End - this.Start + 1);
            Helper h = new Helper(this.TfsVersion);
            string arguments = String.Format(CultureInfo.InvariantCulture, "history \"{0}\" /version:C{1}~{2} /format:brief /noprompt /recursive", this.path, this.Start, this.End);
            ShellExecute shellExecute = new ShellExecute(h.TfExePath, arguments);
            Log.LogMessage(MessageImportance.Normal, string.Format("Executing {0} with {1}", shellExecute.Filename, arguments));
            int returnValue = shellExecute.Execute();

            Log.LogMessage(MessageImportance.Low, shellExecute.StandardOutput);
            switch (returnValue)
            {
                case 1:
                    Log.LogWarning("Exit Code 1. Partial success: " + shellExecute.StandardError.Trim());
                    break;
                case 2:
                    Log.LogError("Exit Code 2. Unrecognized command: " + shellExecute.StandardError.Trim());
                    break;
                case 100:
                    Log.LogError("Exit Code 100. Nothing Succeeded: " + shellExecute.StandardError.Trim());
                    break;
            }

            if (shellExecute.StandardOutput.Length != 0)
            {
                string[] changeLines = shellExecute.StandardOutput.Split('\n');
                for (int i = 2; i < changeLines.Length - 1; i++)
                {
                    string[] changeParts = Helper.SplitColumns(changeLines[i]);
                    string email = changeParts[1];

                    if ((!usersHash.Contains(email)) && (Array.BinarySearch(this.ExcludedUsers, email, new CaseInsensitiveComparer(CultureInfo.InvariantCulture)) < 0))
                    {
                        usersHash.Add(email, null);
                    }
                }
            }

            this.users = new string[usersHash.Count];
            int u = 0;
            foreach (DictionaryEntry de in usersHash)
            {
                this.users[u++] = de.Key.ToString();
            }
        }
    }
}